/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hilton.tigershark;

import java.net.URLEncoder;
import java.util.HashMap;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.provider.ContactsContract.Intents.Insert;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem.OnMenuItemClickListener;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebIconDatabase;
import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.hilton.quickcontrol.PieControl;
import com.hilton.tigershark.UriHandler.UrlData;
import com.hilton.tigershark.bookmarks.AddBookmarkPage;
import com.hilton.tigershark.bookmarks.Bookmarks;
import com.hilton.tigershark.bookmarks.CombinedBookmarkHistoryActivity;
import com.hilton.tigershark.downloads.DownloadHandler;
import com.hilton.tigershark.downloads.DownloadsActivity;
import com.hilton.tigershark.preferences.BrowserPreferencesPage;
import com.hilton.tigershark.titlebar.TitleBar;
import com.hilton.tigershark.titlebar.TitlebarManager;
import com.hilton.tigershark.view.CustomizedWebview;

public class BrowserActivity extends Activity implements View.OnCreateContextMenuListener {
    // activity requestCode
    final static int COMBO_PAGE                 = 1;

    private ActiveTabsPage mActiveTabsPage;
    private TitlebarManager mTitlebarManager;
    private PieControl mPieControl;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
        BrowserSettings.getInstance().updateRlzValues(this);
        if (IntentHandler.handleWebSearchIntent(getIntent())) {
            finish();
            return;
        }
        setContentView(R.layout.custom_screen);
        mContentView = (FrameLayout) findViewById(R.id.main_content);
        mCustomViewContainer = (FrameLayout) findViewById(R.id.fullscreen_custom_content);

        mTabControl = new TabControl(this, mContentView);
        mTitlebarManager = new TitlebarManager(this);
        mTitlebarManager.setProgress(100);
        mPieControl = new PieControl(this, mContentView);
        BrowserSettings.getInstance().setTabControl(mTabControl);
        final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");

        mTabControl.onStart(icicle, getIntent());
        // Start watching the default geolocation permissions
        mSystemAllowGeolocationOrigins = new SystemAllowGeolocationOrigins(getApplicationContext());
        mSystemAllowGeolocationOrigins.start();
        Utility.retainIconsOnStartup(this);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        Tab current = mTabControl.getCurrentTab();
        // When a tab is closed on exit, the current tab index is set to -1.
        // Reset before proceed as Browser requires the current tab to be set.
        if (current == null) {
            // Try to reset the tab in case the index was incorrect.
            current = mTabControl.getTab(0);
            if (current == null) {
                // No tabs at all so just ignore this intent.
                return;
            }
            mTabControl.setCurrentTab(current);
            mTabControl.attachTabToContentView(current);
            mTabControl.resetTitleAndIcon(current.getWebView());
        }

        final String action = intent.getAction();
        final int flags = intent.getFlags();
        if (Intent.ACTION_MAIN.equals(action) ||
                (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
            // just resume the browser
            return;
        }
        // In case the SearchDialog is open.
        ((SearchManager) getSystemService(Context.SEARCH_SERVICE)).stopSearch();
        if (Intent.ACTION_VIEW.equals(action)
                || Intent.ACTION_SEARCH.equals(action)
                || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
                || Intent.ACTION_WEB_SEARCH.equals(action)) {
            // If this was a search request (e.g. search query directly typed into the address bar),
            // pass it on to the default web search provider.
            if (IntentHandler.handleWebSearchIntent(intent)) {
                return;
            }

            UrlData urlData = Utility.getUrlDataFromIntent(intent);
            if (urlData.isEmpty()) {
                urlData = new UrlData(BrowserSettings.getInstance().getHomePage());
            }

            final String appId = intent.getStringExtra(BrowserBridge.EXTRA_APPLICATION_ID);
            if (!TextUtils.isEmpty(urlData.mUrl) && urlData.mUrl.startsWith("javascript:")) {
                // For security sake, always open JavaScript: URIs in new tabs
                mTabControl.openTabAndShow(urlData, true, appId);
                return;
            }
            if ((Intent.ACTION_VIEW.equals(action))
                    && !getPackageName().equals(appId)
                    && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
                Tab appTab = mTabControl.getTabFromId(appId);
                if (appTab != null) {
                    Log.i(LOGTAG, "Reusing tab for " + appId);
                    // Dismiss the subwindow if applicable.
                    mTabControl.dismissSubWindow(appTab);
                    // Since we might kill the WebView, remove it from the
                    // content view first.
                    mTabControl.removeTabFromContentView(appTab);
                    // Recreate the main WebView after destroying the old one.
                    // If the WebView has the same original url and is on that
                    // page, it can be reused.
                    boolean needsLoad = mTabControl.recreateWebView(appTab, urlData);

                    if (current != appTab) {
                        mTabControl.switchToTab(mTabControl.getTabIndex(appTab));
                        if (needsLoad) {
                            mTabControl.loadUrlDataIn(appTab, urlData);
                        }
                    } else {
                        // If the tab was the current tab, we have to attach
                        // it to the view system again.
                        mTabControl.attachTabToContentView(appTab);
                        if (needsLoad) {
                            mTabControl.loadUrlDataIn(appTab, urlData);
                        }
                    }
                    return;
                } else {
                    // No matching application tab, try to find a regular tab
                    // with a matching url.
                    appTab = mTabControl.findUnusedTabWithUrl(urlData.mUrl);
                    if (appTab != null) {
                        if (current != appTab) {
                            mTabControl.switchToTab(mTabControl.getTabIndex(appTab));
                        }
                        // Otherwise, we are already viewing the correct tab.
                    } else {
                        // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
                        // will be opened in a new tab unless we have reached
                        // MAX_TABS. Then the url will be opened in the current
                        // tab. If a new tab is created, it will have "true" for
                        // exit on close.
                        mTabControl.openTabAndShow(urlData, true, appId);
                    }
                }
            } else {
                if (!urlData.isEmpty()) {
                    return;
                }
                mTabControl.dismissSubWindow(current);
                // If the current Tab is being used as an application tab,
                // remove the association, since the new Intent means that it is
                // no longer associated with that application.
                current.setAppId(null);
                mTabControl.loadUrlDataIn(current, urlData);
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!mActivityInPause) {
            Log.e(LOGTAG, "BrowserActivity is already resumed.");
            return;
        }
        mTabControl.onResume();
        mActivityInPause = false;
        if (mWakeLock.isHeld()) {
            mHandler.removeMessages(RELEASE_WAKELOCK);
            mWakeLock.release();
        }

        WebView.enablePlatformNotifications();
        mPieControl.forceToTop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mTabControl.saveState(outState);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mActivityInPause) {
            Log.e(LOGTAG, "BrowserActivity is already paused.");
            return;
        }

        mActivityInPause = true;
        mTabControl.onPause();
        // FIXME: This removes the active tabs page and resets the menu to
        // MAIN_MENU.  A better solution might be to do this work in onNewIntent
        // but then we would need to save it in onSaveInstanceState and restore
        // it in onCreate/onRestoreInstanceState
        if (windowsShowing()) {
            hideWindows(true);
        }

        WebView.disablePlatformNotifications();
    }

    private boolean windowsShowing() {
        return mActiveTabsPage != null;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
            mUploadMessage = null;
        }

        if (mTabControl == null) return;
        mTabControl.onDestroy();

        WebIconDatabase.getInstance().close();

        // Stop watching the default geolocation permissions
        mSystemAllowGeolocationOrigins.stop();
        mSystemAllowGeolocationOrigins = null;
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mTabControl.onConfigurationChanged(newConfig);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mTabControl.freeMemory();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.browser, menu);
        mOptionsMenu = menu;
        updateInLoadMenuItems();
        return true;
    }

    private void updateInLoadMenuItems() {
        if (mOptionsMenu == null) {
            return;
        }
        MenuItem src = mInLoad ? mOptionsMenu.findItem(R.id.stop_menu_id)
                : mOptionsMenu.findItem(R.id.reload_menu_id);
        MenuItem dest = mOptionsMenu.findItem(R.id.stop_reload_menu_id);
        dest.setIcon(src.getIcon());
        dest.setTitle(src.getTitle());
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        mCanChord = true;
        if (inMainWindow()) {
            mMenuIsDown = true;
            updateMainOptionsMenuState(menu);
            return true;
        }
        return false;
    }

    private boolean inMainWindow() {
        return (!windowsShowing() && mCustomView == null);
    }

    private void updateMainOptionsMenuState(Menu menu) {
        menu.setGroupVisible(R.id.MAIN_MENU, true);
        menu.setGroupEnabled(R.id.MAIN_MENU, true);
        final WebView w = mTabControl.getTopWindow();
        boolean canGoForward = false;
        boolean isHome = false;
        if (w != null) {
            canGoForward = w.canGoForward();
            isHome = BrowserSettings.getInstance().getHomePage().equals(w.getUrl());
        }
        menu.findItem(R.id.homepage_menu_id).setEnabled(!isHome);
        menu.findItem(R.id.forward_menu_id).setEnabled(canGoForward);
        menu.findItem(R.id.new_tab_menu_id).setEnabled(mTabControl.canCreateNewTab());

        // decide whether to show the share link option
        PackageManager pm = getPackageManager();
        Intent send = new Intent(Intent.ACTION_SEND);
        send.setType("text/plain");
        ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
        menu.findItem(R.id.share_page_menu_id).setVisible(ri != null);

        boolean isNavDump = BrowserSettings.getInstance().isNavDump();
        final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id);
        nav.setVisible(isNavDump);
        nav.setEnabled(isNavDump);

        boolean debugging = BrowserSettings.getInstance().showDebugSettings();
        final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id);
        counter.setVisible(debugging);
        counter.setEnabled(debugging);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (!mCanChord) {
            // The user has already fired a shortcut with this hold down of the
            // menu key.
            return false;
        }
        if (!mTabControl.hasTopWindow()) {
            return false;
        }
        mMenuIsDown = false;
        switch (item.getItemId()) {
        case R.id.new_tab_menu_id:
            mTabControl.openTabToHomePage();
            break;

        case R.id.bookmarks_menu_id:
            bookmarksOrHistoryPicker();
            break;

        case R.id.active_tabs_menu_id:
            showWindows();
            break;

        case R.id.add_bookmark_menu_id:
            Intent i = new Intent(getApplication(), AddBookmarkPage.class);
            i.putExtra("url", mTabControl.getWebpageUrl());
            i.putExtra("title", mTabControl.getWebpageTitle());
            i.putExtra("thumbnail", Utility.createScreenshot(mTabControl.getTopWindow(), this));
            startActivity(i);
            break;

        case R.id.exit_menu_id:
            exitBrowser();
            break;
        case R.id.stop_reload_menu_id:
            if (mInLoad) {
                mTabControl.stopLoading();
            } else {
                mTabControl.reload();
            }
            break;

        case R.id.forward_menu_id:
            mTabControl.goForward();
            break;

        case R.id.homepage_menu_id:
            mTabControl.dismissSubWindow();
            mTabControl.loadUrl(BrowserSettings.getInstance().getHomePage());
            break;

        case R.id.preferences_menu_id:
            openPreferences();
            break;

        case R.id.find_menu_id:
            mTabControl.showFindDialog();
            break;

        case R.id.title_bar_share_page_url:
        case R.id.share_page_menu_id:
            Tab currentTab = mTabControl.getCurrentTab();
            if (null == currentTab) {
                mCanChord = false;
                return false;
            }
            currentTab.populatePickerData();
            Utility.shareWebpage(this, currentTab.getTitle(), currentTab.getUrl(), currentTab.getFavicon(),
                    Utility.createScreenshot(currentTab.getWebView(), this));
            break;

        case R.id.dump_nav_menu_id:
            mTabControl.debugDump();
            break;

        case R.id.dump_counters_menu_id:
            break;

        case R.id.view_downloads_menu_id:
            viewDownloads();
            break;

        default:
            if (!super.onOptionsItemSelected(item)) {
                return false;
            }
            // Otherwise fall through.
        }
        mCanChord = false;
        return true;
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {
        if (v instanceof TitleBar) {
            return;
        }
        WebView webview = (WebView) v;
        WebView.HitTestResult result = webview.getHitTestResult();
        if (result == null) {
            return;
        }

        int type = result.getType();
        if (type == WebView.HitTestResult.UNKNOWN_TYPE) {
            Log.w(LOGTAG,
                    "We should not show context menu when nothing is touched");
            return;
        }
        if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) {
            // let TextView handles context menu
            return;
        }

        // Note, http://b/issue?id=1106666 is requesting that
        // an inflated menu can be used again. This is not available
        // yet, so inflate each time (yuk!)
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.browsercontext, menu);

        // Show the correct menu group
        String extra = result.getExtra();
        menu.setGroupVisible(R.id.PHONE_MENU,
                type == WebView.HitTestResult.PHONE_TYPE);
        menu.setGroupVisible(R.id.EMAIL_MENU,
                type == WebView.HitTestResult.EMAIL_TYPE);
        menu.setGroupVisible(R.id.GEO_MENU,
                type == WebView.HitTestResult.GEO_TYPE);
        menu.setGroupVisible(R.id.IMAGE_MENU,
                type == WebView.HitTestResult.IMAGE_TYPE
                || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
        menu.setGroupVisible(R.id.ANCHOR_MENU,
                type == WebView.HitTestResult.SRC_ANCHOR_TYPE
                || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE);

        // Setup custom handling depending on the type
        switch (type) {
        case WebView.HitTestResult.PHONE_TYPE:
            menu.setHeaderTitle(Uri.decode(extra));
            menu.findItem(R.id.dial_context_menu_id).setIntent(
                    new Intent(Intent.ACTION_VIEW, Uri
                            .parse(WebView.SCHEME_TEL + extra)));
            Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
            addIntent.putExtra(Insert.PHONE, Uri.decode(extra));
            addIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
            menu.findItem(R.id.add_contact_context_menu_id).setIntent(
                    addIntent);
            menu.findItem(R.id.copy_phone_context_menu_id).setOnMenuItemClickListener(
                    new Copy(extra));
            break;

        case WebView.HitTestResult.EMAIL_TYPE:
            menu.setHeaderTitle(extra);
            menu.findItem(R.id.email_context_menu_id).setIntent(
                    new Intent(Intent.ACTION_VIEW, Uri
                            .parse(WebView.SCHEME_MAILTO + extra)));
            menu.findItem(R.id.copy_mail_context_menu_id).setOnMenuItemClickListener(
                    new Copy(extra));
            break;

        case WebView.HitTestResult.GEO_TYPE:
            menu.setHeaderTitle(extra);
            menu.findItem(R.id.map_context_menu_id).setIntent(
                    new Intent(Intent.ACTION_VIEW, Uri
                            .parse(WebView.SCHEME_GEO
                                    + URLEncoder.encode(extra))));
            menu.findItem(R.id.copy_geo_context_menu_id).setOnMenuItemClickListener(
                    new Copy(extra));
            break;

        case WebView.HitTestResult.SRC_ANCHOR_TYPE:
        case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
            TextView titleView = (TextView) LayoutInflater.from(this)
            .inflate(android.R.layout.browser_link_context_header,
                    null);
            titleView.setText(extra);
            menu.setHeaderView(titleView);
            // decide whether to show the open link in new tab option
            menu.findItem(R.id.open_newtab_context_menu_id).setVisible(
                    mTabControl.canCreateNewTab());
            menu.findItem(R.id.bookmark_context_menu_id).setVisible(
                    Bookmarks.urlHasAcceptableScheme(extra));
            PackageManager pm = getPackageManager();
            Intent send = new Intent(Intent.ACTION_SEND);
            send.setType("text/plain");
            ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
            menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
            if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
                break;
            }
            // otherwise fall through to handle image part
        case WebView.HitTestResult.IMAGE_TYPE:
            if (type == WebView.HitTestResult.IMAGE_TYPE) {
                menu.setHeaderTitle(extra);
            }
            menu.findItem(R.id.view_image_context_menu_id).setIntent(
                    new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
            menu.findItem(R.id.download_context_menu_id).
            setOnMenuItemClickListener(new Download(extra));
            menu.findItem(R.id.set_wallpaper_context_menu_id).
            setOnMenuItemClickListener(new WallpaperHandler(extra, this));
            break;

        default:
            Log.w(LOGTAG, "We should not get here.");
            break;
        }
        mTitlebarManager.hideTitleBar();
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        // chording is not an issue with context menus, but we use the same
        // options selector, so set mCanChord to true so we can access them.
        mCanChord = true;
        int id = item.getItemId();
        boolean result = true;
        switch (id) {
        // For the context menu from the title bar
        case R.id.title_bar_copy_page_url:
            Tab currentTab = mTabControl.getCurrentTab();
            if (null == currentTab) {
                result = false;
                break;
            }
            WebView mainView = currentTab.getWebView();
            if (null == mainView) {
                result = false;
                break;
            }
            copy(mainView.getUrl());
            break;
            // -- Browser context menu
        case R.id.open_context_menu_id:
        case R.id.open_newtab_context_menu_id:
        case R.id.bookmark_context_menu_id:
        case R.id.save_link_context_menu_id:
        case R.id.share_link_context_menu_id:
        case R.id.copy_link_context_menu_id:
            // TODO: implement
            break;

        default:
            // For other context menus
            result = onOptionsItemSelected(item);
        }
        mCanChord = false;
        return result;
    }



    public void openPreferences() {
        Intent intent = new Intent(this, BrowserPreferencesPage.class);
        intent.putExtra(BrowserPreferencesPage.CURRENT_PAGE, mTabControl.getWebpageUrl());
        startActivityForResult(intent, PREFERENCES_PAGE);
    }

    private AlertDialog mExitPromtDialog;
    public void exitBrowser() {
        if (mExitPromtDialog == null) {
            mExitPromtDialog = new AlertDialog.Builder(this)
            .setTitle(R.string.exit_browser)
            .setMessage(R.string.exit_prompt)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setCancelable(true)
            .setPositiveButton(R.string.exit_browser, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                    Process.killProcess(Process.myPid());
                }
            })
            .setNeutralButton(R.string.minimize_browser, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    moveTaskToBack(true);
                }
            })
            .setNegativeButton(R.string.cancel, null)
            .create();
        }
        mExitPromtDialog.show();
    }

    private class Copy implements OnMenuItemClickListener {
        private CharSequence mText;

        public boolean onMenuItemClick(MenuItem item) {
            copy(mText);
            return true;
        }

        public Copy(CharSequence toCopy) {
            mText = toCopy;
        }
    }

    private void copy(CharSequence text) {
    }

    boolean isMenuDown() {
        return mMenuIsDown;
    }

    @Override
    public void onBackPressed() {
        if (windowsShowing()) {
            hideWindows(true);
        } else {
            WebView subwindow = mTabControl.getCurrentSubWindow();
            if (subwindow != null) {
                if (subwindow.canGoBack()) {
                    subwindow.goBack();
                } else {
                    mTabControl.dismissSubWindow();
                }
            } else {
                mTabControl.goBackOnePageOrQuit();
            }
        }
    }

    /**
     * Remove the active tabs page.
     * @param needToAttach If true, the active tabs page did not attach a tab
     *                     to the content view, so we need to do that here.
     */
    /* package */ void hideWindows(boolean needToAttach) {
        mContentView.removeView(mActiveTabsPage);
        mActiveTabsPage = null;
        if (needToAttach) {
            mTabControl.attachTabToContentView(mTabControl.getCurrentTab());
        }
        mTitlebarManager.showTitleBar();
        mTabControl.requestFocus();
        mPieControl.forceToTop();
    }


    /* package */ public void showWindows() {
        mActiveTabsPage = new ActiveTabsPage(this, mTabControl);
        mTabControl.removeTabFromContentView(mTabControl.getCurrentTab());
        mTitlebarManager.hideTitleBar();
        mContentView.addView(mActiveTabsPage, COVER_SCREEN_PARAMS);
        mActiveTabsPage.requestFocus();
    }

    // called by a UI or non-UI thread to post the message
    public void postMessage(int what, int arg1, int arg2, Object obj, long delayMillis) {
        mHandler.sendMessageDelayed(mHandler.obtainMessage(what, arg1, arg2,
                obj), delayMillis);
    }

    // called by a UI or non-UI thread to remove the message
    void removeMessages(int what, Object object) {
        mHandler.removeMessages(what, object);
    }

    // public message ids
    public final static int LOAD_URL                = 1001;
    public final static int STOP_LOAD               = 1002;

    // Message Ids
    private static final int FOCUS_NODE_HREF         = 102;
    private static final int RELEASE_WAKELOCK        = 107;

    static final int UPDATE_BOOKMARK_THUMBNAIL       = 108;

    // Private handler for handling javascript and saving passwords
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case FOCUS_NODE_HREF:
            {
                String url = (String) msg.getData().get("url");
                String title = (String) msg.getData().get("title");
                if (url == null || url.length() == 0) {
                    break;
                }
                HashMap focusNodeMap = (HashMap) msg.obj;
                WebView view = (WebView) focusNodeMap.get("webview");
                // Only apply the action if the top window did not change.
                if (mTabControl.getTopWindow() != view) {
                    break;
                }
                switch (msg.arg1) {
                case R.id.open_context_menu_id:
                case R.id.view_image_context_menu_id:
                    mTabControl.loadUrlFromContext(url);
                    break;
                case R.id.open_newtab_context_menu_id:
                    final Tab parent = mTabControl.getCurrentTab();
                    final Tab newTab = mTabControl.openTab(url);
                    if (newTab != parent) {
                        parent.addChildTab(newTab);
                    }
                    break;
                case R.id.bookmark_context_menu_id:
                    addToBookmarks(url, title);
                    break;
                case R.id.share_link_context_menu_id:
                    Utility.shareUrlLink(getApplication(), url);
                    break;
                case R.id.copy_link_context_menu_id:
                    copy(url);
                    break;
                case R.id.save_link_context_menu_id:
                case R.id.download_context_menu_id:
                    DownloadHandler.onDownloadStart(getApplication(), url, null, null, null, -1);
                    break;
                }
                break;
            }

            case LOAD_URL:
                mTabControl.loadUrlFromContext((String) msg.obj);
                break;

            case STOP_LOAD:
                mTabControl.stopLoading();
                break;

            case RELEASE_WAKELOCK:
                if (mWakeLock.isHeld()) {
                    mWakeLock.release();
                    // if we reach here, Browser should be still in the
                    // background loading after WAKELOCK_TIMEOUT (5-min).
                    // To avoid burning the battery, stop loading.
                    mTabControl.stopAllLoading();
                }
                break;

            case UPDATE_BOOKMARK_THUMBNAIL:
                Utility.updateScreenshot((WebView) msg.obj, getApplication());
                break;
            }
        }

        private void addToBookmarks(String url, String title) {
            Intent intent = new Intent(BrowserActivity.this, AddBookmarkPage.class);
            intent.putExtra("url", url);
            intent.putExtra("title", title);
            startActivity(intent);
        }
    };

    // -------------------------------------------------------------------------
    // Helper function for WebViewClient.
    //-------------------------------------------------------------------------

    // Keep this initial progress in sync with initialProgressValue (* 100)
    // in ProgressTracker.cpp
    public final static int INITIAL_PROGRESS = 10;

    void onPageStarted(WebView view, String url, Bitmap favicon) {
        // when BrowserActivity just starts, onPageStarted may be called before
        // onResume as it is triggered from onCreate. Call resumeWebViewTimers
        // to start the timer. As we won't switch tabs while an activity is in
        // pause state, we can ensure calling resume and pause in pair.
        if (mActivityInPause) mTabControl.resumeWebViewTimers();

        mTabControl.resetLockIcon(url);
        mTabControl.setUrlTitle(url, null);
        mTabControl.setFavicon(favicon);
        // Show some progress so that the user knows the page is beginning to
        // load
        onProgressChanged(view, INITIAL_PROGRESS);
        //        mDidStopLoad = false;
        mTabControl.closeDialogs();
    }

    void onPageFinished(WebView view, String url) {
        // Reset the title and icon in case we stopped a provisional load.
        mTabControl.resetTitleAndIcon(view);
        // Update the lock icon image only once we are done loading
        mTabControl.updateLockIconToLatest();
        // pause the WebView timer and release the wake lock if it is finished
        // while BrowserActivity is in pause state.
        if (mActivityInPause && mTabControl.pauseWebViewTimers()) {
            if (mWakeLock.isHeld()) {
                mHandler.removeMessages(RELEASE_WAKELOCK);
                mWakeLock.release();
            }
        }
    }


    // -------------------------------------------------------------------------
    // Helper function for WebChromeClient
    // -------------------------------------------------------------------------
    void onProgressChanged(WebView view, int newProgress) {
        mTitlebarManager.setProgress(newProgress);

        if (newProgress >= 100) {
            // onProgressChanged() may continue to be called after the main
            // frame has finished loading, as any remaining sub frames continue
            // to load. We'll only get called once though with newProgress as
            // 100 when everything is loaded. (onPageFinished is called once
            // when the main frame completes loading regardless of the state of
            // any sub frames so calls to onProgressChanges may continue after
            // onPageFinished has executed)
            if (mInLoad) {
                mInLoad = false;
                updateInLoadMenuItems();
            }
        } else {
            if (!mInLoad) {
                // onPageFinished may have already been called but a subframe is
                // still loading and updating the progress. Reset mInLoad and
                // update the menu items.
                mInLoad = true;
                updateInLoadMenuItems();
            }
        }
    }

    void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
        // if a view already exists then immediately terminate the new one
        if (mCustomView != null) {
            callback.onCustomViewHidden();
            return;
        }

        // Add the custom view to its container.
        mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER);
        mCustomView = view;
        mCustomViewCallback = callback;
        mContentView.setVisibility(View.GONE);
        // Finally show the custom view container.
        setStatusBarVisibility(false);
        mCustomViewContainer.setVisibility(View.VISIBLE);
        mCustomViewContainer.bringToFront();
    }

    void onHideCustomView() {
        if (mCustomView == null)
            return;
        // Hide the custom view.
        mCustomView.setVisibility(View.GONE);
        // Remove the custom view from its container.
        mCustomViewContainer.removeView(mCustomView);
        mCustomView = null;
        mCustomViewContainer.setVisibility(View.GONE);
        mCustomViewCallback.onCustomViewHidden();
        // Show the content view.
        setStatusBarVisibility(true);
        mContentView.setVisibility(View.VISIBLE);
    }

    Bitmap getDefaultVideoPoster() {
        if (mDefaultVideoPoster == null) {
            mDefaultVideoPoster = BitmapFactory.decodeResource(
                    getResources(), R.drawable.default_video_poster);
        }
        return mDefaultVideoPoster;
    }

    View getVideoLoadingProgressView() {
        if (mVideoProgressView == null) {
            LayoutInflater inflater = LayoutInflater.from(BrowserActivity.this);
            mVideoProgressView = inflater.inflate(
                    R.layout.video_loading_progress, null);
        }
        return mVideoProgressView;
    }

    /*
     * The Object used to inform the WebView of the file to upload.
     */
    private ValueCallback<Uri> mUploadMessage;

    void openFileChooser(ValueCallback<Uri> uploadMsg) {
        if (mUploadMessage != null) return;
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        BrowserActivity.this.startActivityForResult(Intent.createChooser(i,
                getString(R.string.choose_upload)), FILE_SELECTED);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (!mTabControl.hasTopWindow()) return;
        switch (requestCode) {
        case COMBO_PAGE:
            if (resultCode == RESULT_OK && intent != null) {
                String data = intent.getAction();
                Bundle extras = intent.getExtras();
                if (extras != null && extras.getBoolean("new_window", false)) {
                    mTabControl.openTab(data);
                } else {
                    mTabControl.dismissSubWindow();
                    if (data != null && data.length() != 0) {
                        mTabControl.loadUrl(data);
                    }
                }
            }
            // Deliberately fall through to PREFERENCES_PAGE, since the
            // same extra may be attached to the COMBO_PAGE
        case PREFERENCES_PAGE:
            if (resultCode == RESULT_OK && intent != null) {
                String action = intent.getStringExtra(Intent.EXTRA_TEXT);
                if (BrowserSettings.PREF_CLEAR_HISTORY.equals(action)) {
                    mTabControl.removeParentChildRelationShips();
                }
            }
            break;
            // Choose a file from the file picker.
        case FILE_SELECTED:
            if (null == mUploadMessage) break;
            Uri result = intent == null || resultCode != RESULT_OK ? null
                    : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
            break;
        default:
            break;
        }
        mTabControl.requestFocus();
    }

    /*
     * This method is called as a result of the user selecting the options
     * menu to see the download window. It shows the download window on top of
     * the current window.
     */
    private void viewDownloads() {
        Intent intent = new Intent(getApplication(), DownloadsActivity.class);
        startActivity(intent);
    }


    private void setStatusBarVisibility(boolean visible) {
        int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN;
        getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }

    private TabControl      mTabControl;
    private FrameLayout     mContentView;
    private View            mCustomView;
    private FrameLayout     mCustomViewContainer;
    private WebChromeClient.CustomViewCallback mCustomViewCallback;
    private Menu mOptionsMenu;

    // Used to prevent chording to result in firing two shortcuts immediately
    // one after another.  Fixes bug 1211714.
    boolean mCanChord;

    private boolean mInLoad;


    /* package */ boolean mActivityInPause = true;

    private boolean mMenuIsDown;

    /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
        new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
    /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
        new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT,
                Gravity.CENTER);
    private final static String LOGTAG = "browser";

    private PowerManager.WakeLock mWakeLock;
    private final static int WAKELOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes

    private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins;

    final static int PREFERENCES_PAGE           = 3;
    final static int FILE_SELECTED              = 4;

    // the default <video> poster
    private Bitmap mDefaultVideoPoster;
    // the video progress view
    private View mVideoProgressView;


    private class Download implements OnMenuItemClickListener {
        private String mText;

        public boolean onMenuItemClick(MenuItem item) {
            DownloadHandler.onDownloadStart(getApplication(), mText, null, null, null, -1);
            return true;
        }

        public Download(String toDownload) {
            mText = toDownload;
        }
    }

    public Tab getActiveTab() {
        return mTabControl.getCurrentTab();
    }
    public void openTabToHomePage() {
        mTabControl.openTabToHomePage();
    }

    public String getWebpageTitle() {
        return mTabControl.getWebpageTitle();
    }

    public String getWebpageUrl() {
        return mTabControl.getWebpageUrl();
    }

    public void stopLoading() {
        mTabControl.stopLoading();
    }

    public void loadUrl(String url) {
        mTabControl.loadUrl(url);
    }

    public void focusOnWebpage() {
        mTabControl.requestFocus();
    }

    public void bookmarksOrHistoryPicker() {
        WebView current = mTabControl.getCurrentWebView();
        if (current == null) {
            return;
        }
        Intent intent = new Intent(this, CombinedBookmarkHistoryActivity.class);
        String title = current.getTitle();
        String url = current.getUrl();
        Bitmap thumbnail = Utility.createScreenshot(current, this);
        if (null == url) {
            url = BrowserSettings.getInstance().getHomePage();
        }
        // In case the web page has not yet received its associated title.
        if (title == null) {
            title = url;
        }
        intent.putExtra("title", title);
        intent.putExtra("url", url);
        intent.putExtra("thumbnail", thumbnail);
        intent.putExtra("disable_new_window", !mTabControl.canCreateNewTab());
        //        intent.putExtra("touch_icon_url", current.getTouchIconUrl());
        //        if (startWithHistory) {
        //            intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
        //                    CombinedBookmarkHistoryActivity.HISTORY_TAB);
        //        }
        startActivityForResult(intent, COMBO_PAGE);
    }

    public void attachTitlebarTo(CustomizedWebview topWindow) {
        mTitlebarManager.attachTo(topWindow);
    }

    public void setDisplayTitle(String t) {
        mTitlebarManager.setDisplayTitle(t);
    }

    public void setFavicon(Bitmap icon) {
        mTitlebarManager.setFavicon(icon);
    }

    public void toggleLockIcon(Drawable d) {
        mTitlebarManager.toggleLockIcon(d);
    }

    public void attachPieControl() {
        mPieControl.attach();
    }

    public void detachPieControl() {
        mPieControl.detach();
    }

    public void showTitlebar() {
        mTitlebarManager.showTitleBar();
    }

    public void hideTitlebar() {
        mTitlebarManager.hideTitleBar();
    }

    public void closeDialogs() {
        mTabControl.closeDialogs();
    }

    public boolean dialogsUp() {
        return mTabControl.dialogsUp();
    }
}